package com.ups.singlearner.views;

import java.lang.Thread.State;
import java.util.ArrayList;
import java.util.List;

import com.ups.singlearner.ComposedNote;
import com.ups.singlearner.Composition;
import com.ups.singlearner.LineProgress;
import com.ups.singlearner.R;
import com.ups.singlearner.SurfaceViewThread;
import com.ups.singlearner.activities.CompositionActivity;
import com.ups.singlearner.util.NoteUtil;
import com.ups.singlearner.util.NoteUtil.Note;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;

public class CustomSurfaceView extends SurfaceView implements
		SurfaceHolder.Callback, OnTouchListener {

	// Log
	private final String TAG = "CustomSurfaceView";
	// Marge pour afficher la vue
	private final int LINE_TOP_MARGIN = 20;
	// La marge des notes
	private final int LINE_RIGHT_MARGIN = 25;
	// Texte commence à
	private final int TEXT_RIGHT_MARGIN = LINE_RIGHT_MARGIN + 6;
	// Savoir si on dessine sur la surface
	private boolean is_drawing = false;
	// Afficher les résulats sur la surface
	private boolean draw_result = false;
	// Coordonées du premier touch
	private int drawing_x_pos;
	private int drawing_y_pos;
	// Thread qui permet de dessiner sur la surface
	private SurfaceViewThread thread;

	/*
	 * TRICKY, because it's a custom widget, need to pass attribute to
	 * constructor
	 */
	public CustomSurfaceView(Context c, AttributeSet set) {
		super(c, set);
		getHolder().addCallback(this);
		setOnTouchListener(this);

		// Lancer le thread
		thread = new SurfaceViewThread(getHolder(), this);
	}

	// Pour dessiner une note sur le canvas
	@Override
	public void onDraw(Canvas canvas) {
		Paint paint = new Paint();
		// Tout repaindre en noir
		paint.setColor(Color.TRANSPARENT);
		canvas.drawRect(new Rect(0, 0, canvas.getWidth(), canvas.getHeight()),
				paint);
		
			// Dessiner les lignes des notes blanches fines + les noms des notes
			drawNoteLines(canvas);
		if (!draw_result){	
			// Dessiner les notes composés
			drawComposedNotes(canvas);
		}
		// Dessiner la barre de défilement
		// drawComposedLine(canvas);
		// Afficher le résultat sur la surface
		if (draw_result)
			drawResult(canvas);
	}

	// Dessiner les lignes blanches + les noms des notes
	private void drawNoteLines(Canvas canvas) {
		Paint line_paint = new Paint();
		line_paint.setColor(Color.BLACK);

		Paint text_paint = new Paint(line_paint);
		text_paint.setTextSize(20);

		Note[] notes = NoteUtil.Note.values();

		for (int i = 0; i < 7; i++) {
			canvas.drawLine(0, i * (getHeight() / 7) + LINE_TOP_MARGIN,
					getWidth() - LINE_RIGHT_MARGIN, i * (getHeight() / 7)
							+ LINE_TOP_MARGIN, line_paint);
			canvas.drawText(notes[i].name(), getWidth() - TEXT_RIGHT_MARGIN, i
					* (getHeight() / 7) + LINE_TOP_MARGIN, text_paint);
		}
	}

	ImageView drawingImageView;

	// Dessiner les notes composés
	private void drawComposedNotes(Canvas canvas) {
		// Log.d("draw", "called general");

		Paint line_paint = new Paint();
		line_paint.setColor(Color.BLACK);
		line_paint.setStrokeWidth(10);

		Paint line_paint_ = new Paint();
		line_paint_.setColor(Color.YELLOW);
		line_paint_.setStrokeWidth(5);
		
		synchronized (Composition.getInstance()) {
			List<ComposedNote> notes = Composition.getInstance().getNotes();
			LineProgress line = Composition.getInstance().getLineProgress();

			for (ComposedNote note : notes) {
				// Log.d("draw", "called");
				int note_idx = (note.getY() + ((getHeight() / 7) / 2) - LINE_TOP_MARGIN)
						/ (getHeight() / 7);
				int y_draw = note_idx * (getHeight() / 7) + LINE_TOP_MARGIN;
				canvas.drawLine(note.getBegin(), y_draw,
						note.getBegin() + note.getDuration(), y_draw,
						line_paint);

				canvas.drawLine(line.getxStart(), line.getyStart(),
						line.getxStop(), line.getyStop(), line_paint_);

				/*
				 * Bitmap _scratch =
				 * BitmapFactory.decodeResource(getResources(),
				 * R.drawable.line_progress); canvas.drawBitmap(_scratch,
				 * note.getBegin() + note.getDuration(), 0, null);
				 */
			}
		}
		// canvas.drawLine(x_begin, y, x_end, y, line_paint);
	}

	// Dessiner les notes composés
	private void drawComposedLine(Canvas canvas) {
		// Log.d("draw", "called general");

		Paint line_paint_ = new Paint();
		line_paint_.setColor(Color.YELLOW);
		line_paint_.setStrokeWidth(5);

		synchronized (Composition.getInstance()) {

			canvas.drawLine(0, 0, 0, getHeight(), line_paint_);
		}
	}
	
	// Affiche le resultat après avoir chanté
	private void drawResult(Canvas canvas) {
		Paint paint = null;

		Paint good_note_paint = new Paint();
		good_note_paint.setColor(Color.GREEN);
		good_note_paint.setStrokeWidth(5);

		Paint bad_note_paint = new Paint();
		bad_note_paint.setColor(Color.RED);
		bad_note_paint.setStrokeWidth(5);

		ArrayList<ComposedNote> notes = Composition.getInstance().getNotes(); // notes
																				// composés
		ArrayList<ComposedNote> sungNotes = Composition.getInstance() // notes
																		// chantés
				.getSungNotes();
		int size = Math.min(sungNotes.size(), notes.size());
		int result = 0;
		for (int i = 0; i < size; i++) {
			ComposedNote note = notes.get(i);
			ComposedNote sungNote = sungNotes.get(i);

			if (note.getNote().equals(sungNote.getNote())) {
				// draw in green
				paint = good_note_paint;
				result++;
			} else {
				// draw in red
				paint = bad_note_paint;
			}

			int note_idx = (note.getY() + ((getHeight() / 7) / 2) - LINE_TOP_MARGIN)
					/ (getHeight() / 7);
			int y_draw = note_idx * (getHeight() / 7) + LINE_TOP_MARGIN;
			
			canvas.drawLine(note.getBegin(), y_draw,
					note.getBegin() + note.getDuration(), y_draw,
					paint);
		}
		float resu = ((float)result/(float)size)*100.0f;
		if(resu >= 0.0)
			CompositionActivity.resultatView.setText("Resultat = "+resu+" %");
	}

	// A chaque fois qu'il y a une modification sur la surface
	public void surfaceChanged(SurfaceHolder holder, int format, int width,
			int height) {
		// Log.d(TAG, "SurfaceChanged");
		thread.setRunning(true);
		// State s = thread.getState();
		// if (s == State.NEW || s == State.RUNNABLE || s == State.TERMINATED)
		try {
			// thread.start();
		} catch (Exception e) {
			System.out.println(e);
		}
	}

	// Lorsque la surface est crée
	public void surfaceCreated(SurfaceHolder holder) {
		// Log.d(TAG, "SurfaceCreated");
		thread.setRunning(true);
		/*
		 * if (!thread.isAlive()) thread.start();
		 */
		State s = thread.getState();
		// if (s == State.NEW || s == State.RUNNABLE)
		try {
			thread.start();
			thread.setDaemon(true);
		} catch (Exception e) {
			Log.d(TAG, e.getMessage());
		}
	}

	// Lorsqu'on quitte l'application
	public void surfaceDestroyed(SurfaceHolder holder) {
		// simply copied from sample application LunarLander:
		// we have to tell thread to shut down & wait for it to finish, or else
		// it might touch the Surface after we return and explode
		boolean retry = true;
		thread.setRunning(false);
		while (retry) {
			try {
				thread.join();
				retry = false;
			} catch (InterruptedException e) {
				// what can we do ?
			}
		}
	}

	public boolean onTouch(View v, MotionEvent event) {
		Log.d("onTouch", "touch : " + event.getX() + ", " + event.getY());
		
		if(draw_result){
			draw_result = false;
			Composition.getInstance().getNotes().clear();
			Composition.getInstance().getSungNotes().clear();
			CompositionActivity.recordButton.setEnabled(true);
			CompositionActivity.resultatView.setText("");
		}
		
		
		int action = event.getAction() & MotionEvent.ACTION_MASK;

		switch (action) {
		case MotionEvent.ACTION_DOWN:
			Log.d("Event", "action down");
			if (is_drawing) {
				// shouldn't happen
				break;
			}
			is_drawing = true;
			drawing_x_pos = (int) event.getX();
			drawing_y_pos = (int) event.getY();
			break;

		case MotionEvent.ACTION_UP:
			Log.d("Event", "action up");
			if (!is_drawing) {
				// shouldn't happen
				break;
			}
			is_drawing = false;

			// try to determine note with position
			Note n = determineNote((int) event.getX(), (int) event.getY());

			int duration = (int) Math.abs(drawing_x_pos - event.getX());
			// redraw if necessary
			if (n != null) {
				int x_begin = Math.min(drawing_x_pos, (int) event.getX());
				ComposedNote cn = new ComposedNote(n, x_begin, duration,
						(int) event.getY());
				Composition.getInstance().addNote(cn);
				LineProgress line = new LineProgress();

				line.setxStart(x_begin + duration);
				line.setyStart(0);
				line.setxStop(x_begin + duration);
				line.setyStop(getHeight());

				Composition.getInstance().setLineProgress(line);
				// forcer le réaffichage
				postInvalidate();
			}
			break;
		case MotionEvent.ACTION_MOVE:
			// TODO addNote() here when the Composition class will be smart
			// enough to
			// modify note instead of always add new
			// TODO After addNote(), add postInvalidate(); to force redrawing

			// try to determine note with position
			Note n_ = determineNote((int) event.getX(), (int) event.getY());

			int duration_ = (int) Math.abs(drawing_x_pos - event.getX());
			// redraw if necessary
			if (n_ != null) {
				int x_begin = Math.min(drawing_x_pos, (int) event.getX());
				ComposedNote cn = new ComposedNote(n_, x_begin, duration_,
						(int) event.getY());
				Composition.getInstance().addNote(cn);
				LineProgress line = new LineProgress();

				line.setxStart(x_begin + duration_);
				line.setyStart(0);
				line.setxStop(x_begin + duration_);
				line.setyStop(getHeight());

				Composition.getInstance().setLineProgress(line);
				// forcer le réaffichage
				postInvalidate();
			}
			break;
		default:
			// Log.d("Event : ", ""+action);
			break;
		}
		return true;
	}

	// Déterminer la note dessiné
	private NoteUtil.Note determineNote(int x, int y) {

		// check if the same note has been drawn
		if (Math.abs(drawing_y_pos - y) < 20) {
			Note[] notes = NoteUtil.Note.values();
			/*
			 * Log.d("Note : ", notes[(y + ((getHeight() / 7) / 2) -
			 * LINE_TOP_MARGIN) / (getHeight() / 7)].name());
			 */
			int size = (y + ((getHeight() / 7) / 2) - LINE_TOP_MARGIN)
					/ (getHeight() / 7);
			if (size < 7)
				return notes[size];
			else
				return notes[6];
		}

		return null;

	}

	public boolean isDraw_result() {
		return draw_result;
	}

	public void setDraw_result(boolean draw_result) {
		this.draw_result = draw_result;
	}

	public SurfaceViewThread getThread() {
		return thread;
	}
}
